﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CLFramework.Infrastructure.PlugIns
{
    public class PluginEngine
    {
        ReflectionPluginEngine reflectionEngine = new ReflectionPluginEngine();
        private List<PluginContainer> plugins;

        public PluginEngine()
            : this(new List<PluginContainer>())
        {}

        public PluginEngine(List<PluginContainer> plugins)
        {
            this.plugins = plugins;
        }
        
        public TResult Execute<TResult>(string pluginName, string operation, params object[] input)
        {
            return Execute<TResult>(pluginName, operation, getInstalledAndEnabled(this), input);
        }

        public TResult Execute<TResult>(string pluginName, string operation, object parameters, Func<PluginContainer, bool> predicate)
        {
            if (predicate != null)
                return reflectionEngine.Execute<TResult>(plugins.Where(predicate), pluginName, operation, parameters);

            return reflectionEngine.Execute<TResult>(plugins, pluginName, operation, parameters);
        }

        public TResult ExecuteFirst<TResult>(string operation, object parameters, Func<PluginContainer, bool> predicate)
        {
            if (predicate != null)
                return reflectionEngine.ExecuteFirst<TResult>(plugins.Where(predicate), operation, parameters);

            return reflectionEngine.ExecuteFirst<TResult>(plugins, operation, parameters);
        }

        public void ExecuteAll(string operation, object parameters)
        {
            ExecuteAll(operation, parameters, getInstalledAndEnabled(this));
        }

        public void ExecuteAll(string operation, object parameters, Func<PluginContainer, bool> predicate)
        {
            if (predicate != null)
                reflectionEngine.ExecuteAll(plugins.Where(predicate), operation, parameters);
            else
                reflectionEngine.ExecuteAll(plugins, operation, parameters);
        }

        public T GetMethod<T>(string operation) where T : class
        {
            return GetMethod<T>(operation, getInstalledAndEnabled(this));
        }

        public T GetMethod<T>(string operation, Func<PluginContainer, bool> predicate) where T : class
        {
            if (predicate != null)
                return reflectionEngine.GetMethod<T>(plugins.Where(predicate), operation);

            return reflectionEngine.GetMethod<T>(plugins, operation);
        }
        
        public T Process<T>(string operation, T input) where T : class
        {
            return Process<T>(operation, input, getInstalledAndEnabled(this));
        }

        public T Process<T>(string operation, T input, Func<PluginContainer, bool> predicate) where T : class
        {
            if (predicate == null)
                return reflectionEngine.Process(plugins, operation, input);

            return reflectionEngine.Process(plugins.Where(predicate), operation, input);
        }

        public bool AllTrue(string operation, object parameters, Func<PluginContainer, bool> predicate)
        {
            if (predicate != null)
                return reflectionEngine.AllTrue(plugins.Where(predicate), operation, parameters);

            return reflectionEngine.AllTrue(plugins, operation, parameters);
        }

        public bool AnyTrue(string operation, object parameters, Func<PluginContainer, bool> predicate)
        {
            if (predicate != null)
                return reflectionEngine.AnyTrue(plugins.Where(predicate), operation, parameters);

            return reflectionEngine.AnyTrue(plugins, operation, parameters);
        }
        
        private static Func<PluginContainer, bool> GetNotInstalled(PluginEngine pluginEngine)
        {
            return p => p.Instance == null || !p.Enabled;
        }

        private static Func<PluginContainer, bool> GetInstalled(PluginEngine pluginEngine)
        {
            return p => p.Instance != null && p.Enabled;
        }

        private static Func<PluginContainer, bool> getInstalledAndEnabled(PluginEngine pluginEngine)
        {
            return p => p.Instance != null && p.Instance is PlugInBase && p.Enabled;
        }
    }
}
